/**
 * ex4. 函数与自定义类型与闭包
 *
 * @author cyy
 * @date 2020/10/29
 */
package main

import "fmt"

/*
	go语言程序执行时会自动触发包内部init()函数的调用
		无参数，无返回值，在执行时自动被调用执行，不能在代码中主动调用
		包初始化执行的顺序：全局声明 -> init() -> main()
*/
func init() {
	fmt.Println("ex11：init")
}

/* ================== 函数的基本使用 ================== */
// 普通函数的定义
func getTwoSum(a int, b int) int {
	return a + b
}

// 可变参数
func getSum(a ...int) int {
	sum := 0
	for _, i := range a {
		sum += i
	}
	return sum
}

// 类型简写：如果入参类型相同，可以简写入参的数据类型
func getSub(a /* int */, b int) int {
	return a - b
}

// 支持多个返回值
func getSumAndSub(a int, b int) (int, int) {
	return a + b, a - b
}

// 返回值命名：函数定义时可以给返回值名命，并在函数体中直接使用这些变量并return
func getMultiAndDiv(a int, b int) (multi int, div int) {
	multi = a * b
	div = a / b
	return /* multi, div */ // 当返回值被命名时，return语句可直接省略return的值
}

// 修改返回值返回顺序
func getMultiAndDiv2(a int, b int) (multi int, div int) {
	multi = a * b
	div = a / b
	return div, multi
}

/* ================== 结构体自定义函数 ================== */
// 值类型接收者
type Phone struct {
	Name string
}

func (p *Phone) start() {
	fmt.Println(p.Name, "开机")
}

func (p *Phone) stop() {
	fmt.Println(p.Name, "关机")
}

/* ================== 函数类型与变量 ================== */
// 自定义类型
type cal func(int, int) int
type myInt int

type myFunc func() int

/* ================== 闭包 ================== */
func add() myFunc {
	i := 1 // i 常驻内存，不污染全局
	return func() int {
		i++
		return i
	}
}

func main() {

	fmt.Println("============================ 函数的基本使用 ============================")
	/*
		func 函数名(入参) (返回结果) {
			// 函数体
		}
	*/
	fmt.Println("\n---------------------------- 测试命名返回值顺序交换所带来的改变 ----------------------------")
	multi, div := getMultiAndDiv(4, 2)
	fmt.Printf("未交换顺序，multi = %v, dic = %v\n", multi, div)
	multi2, div2 := getMultiAndDiv2(4, 2)
	fmt.Printf("交换顺序后，multi = %v, dic = %v\n", multi2, div2)

	fmt.Println("\n---------------------------- 匿名函数 ----------------------------")
	/*
		匿名函数 —— 格式
			func(参数) (返回结果){
				// 函数体
			}
	*/
	// 匿名函数
	f := func() {
		fmt.Println("匿名函数")
	}

	// 匿名自执行函数
	func(a, b int) {
		fmt.Println("匿名自执行函数", a+b)
	}(1, 1)

	/*
		函数作为参数传递
	*/
	fmt.Println("\n---------------------------- 函数作为参数传递 ----------------------------")
	// 函数作为参数
	fmt.Println(calculate(1, 1, getSub))
	// 匿名函数作为参数
	fmt.Println(calculate(1, 1, func(a int, b int) int {
		return a + b - a*b/a
	}))
	// 函数作为回参
	fmt.Println(calculate(1, 1, funcChoose("/")))

	fmt.Println("\n---------------------------- 结构体自定义函数 ----------------------------")
	p := &Phone{Name: "OPPO"}
	p.start()
	p.stop()

	fmt.Println("\n============================ 自定义类型 ============================")
	fmt.Println("\n---------------------------- 自定义类型 ----------------------------")
	var c1 cal = getTwoSum
	fmt.Printf("自定义函数类型的类型：%T\n", c1) // 自定义的 main.calculate 类型
	c2 := getTwoSum
	fmt.Printf("自定义函数的类型：%T\n", c2) // function类型 func(int, int) int
	var c3 myInt
	fmt.Printf("自定义变量类型的类型：%T\n", c3) // main.myInt
	fmt.Printf("匿名函数的类型：%T\n", f)     // func(int, int)

	fmt.Println("\n============================ 闭包 ============================")
	fn := add()       // 此处相当于执行了i:=1，并且i常驻内存，fn被赋值为函数 func() int {i++; return i}，每一次调用fn()，都会执行一次i++和return i
	fmt.Println(fn()) // 2
	fmt.Println(fn()) // 3
	fmt.Println(fn()) // 4

}

// 函数作为参数
func calculate(a, b int, f cal /* 等同于 func(int, int) int */) int {
	return f(a, b)
}

// 函数作为返回结果
func funcChoose(s string) cal {
	switch s {
	case "+":
		return getTwoSum
	case "-":
		return getSub
	case "*": // 匿名函数作为返回结果
		return func(a int, b int) int {
			return a * b
		}
	case "/": // 匿名函数作为返回结果
		return func(a int, b int) int {
			return a / b
		}
	default:
		return nil
	}
}
